/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

// 状态
const PENDING = 'pending';   //等待
const FULFILLED = 'fulfilled';  //成功
const REJECTED = 'rejected';   //失败
// promise状态改变 就不会再更改  并且只能从等待转换状态
// 等待 => 成功
// 等待 => 失败

class MyPromise {
    // promise会传入一个生成器  接受成功和失败方法
    constructor(executor) {
        try {
            executor(this.resolve, this.reject);
            // executor(resolve.bind(this), reject.bind(this)); 当不用箭头函数时
        } catch (e) {
            this.reject(e)
        }

    }
    // 用箭头函数的原因是this指向   调用resolve时是直接调用   所以会有this（window等）指向问题
    // resolve和reject是用来改变 promise 的状态的  
    status = PENDING;  //初始化状态为等待
    value = undefined;   //成功返回的结果
    reason = undefined;  //失败的原因
    //保存成功和失败回调
    successcallback = [];  //成功回调  数组：多次调用
    failcallback = [];  //失败回调 数组：多次调用
    resolve = value => {
        // 状态更改不可改变  判断是否是从等待转换过来的
        if (this.status != PENDING) return;
        // 改变promise的状态为成功
        this.status = FULFILLED;
        // 将 成功返回的结果 存储  方便成功后的回调函数 调用
        this.value = value;
        // 当异步任务结束时 看successcallback的长度 是否为0
        while (this.successcallback.length) this.successcallback.shift()(this.value)
    }
    reject = reason => {
        // 状态更改不可改变  判断是否是从等待转换过来的
        if (this.status != PENDING) return;
        // 改变promise的状态为失败
        this.status = REJECTED;
        // 将 失败的原因 存储  方便成功后的回调函数 调用
        this.reason = reason;
        // 当异步任务结束时 failcallback 是否存在
        while (this.failcallback.length) this.failcallback.shift()(this.reason)

    }
    // 方法 存放的是成功回调和失败回调
    then(succesback, failback) {
        // 将参数变为可选参数
        succesback = succesback ? succesback : value => value;
        failback = failback ? failback : reason => { throw reason };
        // 实现then方法的链式调用  就是返回一个Promise（这样才能调用then方法）
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        // 当状态为成功时  调用成功的回调
                        let hui = succesback(this.value);  //保存这个then方法的 回调
                        // resolve(hui);  //带着这个 个then的 回调
                        resolvePromise(hui, promise2, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }

                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        // 当状态为失败时  调用失败的回调
                        let hui = failback(this.reason);  //保存这个then方法的 回调
                        // resolve(hui);  //带着这个 个then的 回调
                        resolvePromise(hui, promise2, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            } else {
                //当前是 处理异步的情况
                // 当走到这里时 你无法知道一异步之后 该调用成功还是失败  所以将成功和失败 回调 保存起来先
                this.successcallback.push(() => {
                    setTimeout(() => {
                        try {
                            // 当状态为成功时  调用成功的回调
                            let hui = succesback(this.value);  //保存这个then方法的 回调
                            // resolve(hui);  //带着这个 个then的 回调
                            resolvePromise(hui, promise2, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }

                    }, 0)
                });
                this.failcallback.push(() => {
                    setTimeout(() => {
                        try {
                            // 当状态为失败时  调用失败的回调
                            let hui = failback(this.reason);  //保存这个then方法的 回调
                            // resolve(hui);  //带着这个 个then的 回调
                            resolvePromise(hui, promise2, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                });
            }
        })
        return promise2;
    }
    // 失败回调
    catch(failback) {
        return this.then(undefined, failback)
    }
    // 无论结果是成功 还是失败  都会返回一个结果
    finally(callback) {
        return this.then(value => {
            // 这里调用 resolve 静态方法
            return MyPromise.resolve(callback()).then(() => value)
        }, reason => {
            return MyPromise.resolve(callback()).then(reason => reason)
        })
    }

    //静态方法  允许按照异步代码调用的顺序 返回 相应结果的顺序
    static all(array) {
        let result = []; //结果数组
        let index = 0;  //判断异步是否执行完成  就是判断index 得值 是否与 result 的长度相等

        return new MyPromise((resolve, reject) => {
            function add(i, value) {
                result[i] = value;
                index++;
                if (index == array.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < array.length; i++) {
                let current = array[i];
                if (current instanceof MyPromise) {
                    //是promise对象
                    current.then(value => add(i, value), reason => reject(reason))
                } else {
                    //普通值
                    add(i, current);
                }
            }

        })

    }
    //返回一个解析过的Promise对象，如果参数本身就是一个Promise对象，则直接返回这个Promise对象
    static resolve(value) {
        if (value instanceof MyPromise) return value;
        return new MyPromise((resolve, reason) => {
            resolve(value)
        })
    }
    //返回一个解析过的Promise对象，如果参数本身就是一个Promise对象，则直接返回这个Promise对象
    static reject(value) {
        if (value instanceof MyPromise) return value;
        return new MyPromise((resolve, reason) => {
            reason(value)
        })
    }
}


//判断上一个返回的回调（hui）  是否是promise对象
//如果是普通值 就直接调用 resolve()
//如果是promise对象  查看promise返回的结果  调用then方法
//再根据返回的结果  调用相应的成功方法和失败方法
function resolvePromise(hui, promise2, resolve, reject) {
    if (promise2 === hui) {
        return reject(new TypeError('lsdhflsdb'))
    }
    if (hui instanceof MyPromise) {

        // 判断是否是promise 对象
        hui.then(resolve, reject)

    } else {
        // 否则是 普通值
        resolve(hui)
    }
}

module.exports = MyPromise;